package test.http;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.kqueue.KQueue;
import io.netty.channel.kqueue.KQueueServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;

import java.net.InetSocketAddress;

public class NettyHttpPlainTextServer {
    public enum IoMultiplexer {
        EPOLL, KQUEUE, JDK
    }

    static {
        ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
    }

    private final int port;

    public NettyHttpPlainTextServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        // Configure the server.
        try {
            if (Epoll.isAvailable()) {
                System.out.println("Running with epoll");
                doRun(new EpollEventLoopGroup(16), EpollServerSocketChannel.class, IoMultiplexer.EPOLL);
                return;
            }
        } catch (Throwable ignore) {}

        try {
            if (KQueue.isAvailable()) {
                System.out.println("Running with KQueue");
                doRun(new EpollEventLoopGroup(), KQueueServerSocketChannel.class, IoMultiplexer.KQUEUE);
                return;
            }
        } catch (Throwable ignore) {}

        System.out.println("Running with JDK Multiplexer");
        doRun(new NioEventLoopGroup(), NioServerSocketChannel.class, IoMultiplexer.JDK);
    }

    private void doRun(EventLoopGroup loupGroup, Class<? extends ServerChannel> serverChannelClass, IoMultiplexer multiplexer) throws InterruptedException {
        try {
            InetSocketAddress inet = new InetSocketAddress(port);

            ServerBootstrap b = new ServerBootstrap();

            if (multiplexer == IoMultiplexer.EPOLL) {
                b.option(EpollChannelOption.SO_REUSEPORT, true);
            }

            b.option(ChannelOption.SO_BACKLOG, 8192);
            b.option(ChannelOption.SO_REUSEADDR, true);
            b.group(loupGroup).channel(serverChannelClass).childHandler(new PlainTextChannelInitializer(loupGroup.next()));
            b.childOption(ChannelOption.SO_REUSEADDR, true);

            Channel ch = b.bind(inet).sync().channel();

            System.out.printf("Httpd started. Listening on: %s%n", inet.toString());

            ch.closeFuture().sync();
        } finally {
            loupGroup.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new NettyHttpPlainTextServer(port).run();
    }
}
